package com.sservice.web.wx;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.sservice.base.StringUtils;
import com.sservice.common.Constants;
import com.sservice.model.wx.Template;
import com.sservice.model.wx.TemplateParam;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by suweiming on 2017/12/20.
 */

public class AdvancedUtil {

    public static Token token = null; // 缓存token
    public static String time = null;
    public static String jsapi_ticket = null;
    public static String wxAppId = null;
    public static String wxAppSecret = null;
    public static String pcAppId = null;
    public static String pcAppSecret = null;
    private static Logger logger = LoggerFactory.getLogger(AdvancedUtil.class);
    public AdvancedUtil advancedUtil = null;

    public static void senderTmplSms(Template temp) {
        Template tem = temp;
        List<TemplateParam> tp = temp.getTemplateParamList();
        if (Constants.domain.indexOf("localhost") > -1) { //  调试模式
            for (int i = 0; i < tp.size(); i++) {
                TemplateParam tp1 = tp.get(i);
                System.out.println("==========" + tp1.getValue());
            }
        }
        Token token = AdvancedUtil.getAccessTokenUtil();
        logger.debug("【调试】发送模板：" + tem.toJSON());
        if (token != null) {
            sendTemplateMsg(token.getAccessToken(), tem);
        } else {
            logger.debug("【调试】发送模板：" + tem.toJSON());
        }
    }

    public static boolean sendTemplateMsg(String token, Template template) {

        boolean flag = false;

        String requestUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
        requestUrl = requestUrl.replace("ACCESS_TOKEN", token);

        JSONObject jsonResult = AdvancedUtil.httpsRequest(requestUrl, "POST", template.toJSON());
        if (jsonResult != null) {
            int errorCode = jsonResult.getIntValue("errcode");
            String errorMessage = jsonResult.getString("errmsg");
            if (errorCode == 0) {
                flag = true;
            } else {
                logger.debug("模板消息发送失败:" + errorCode + "," + errorMessage + "," + template.getTemplateId());
                flag = false;
            }
        } else {
            logger.debug("jsonResult: is null ->", jsonResult);
        }
        return flag;
    }

    /**
     * @return
     */
    public static JSONObject getParam(String url) {

        token = AdvancedUtil.getAccessTokenUtil();
        jsapi_ticket = AdvancedUtil.getJsApiTicket(token.getAccessToken());
        JSONObject params = sign(jsapi_ticket, url);
        params.put("appid", AdvancedUtil.wxAppId);
        return params;
    }

    public static JSONObject sign(String jsapi_ticket, String url) {
        JSONObject ret = new JSONObject();
        String nonce_str = create_nonce_str();
        String timestamp = create_timestamp();
        String str;
        String signature = "";

        //注意这里参数名必须全部小写，且必须有序
        str = "jsapi_ticket=" + jsapi_ticket +
                "&noncestr=" + nonce_str +
                "&timestamp=" + timestamp +
                "&url=" + url;

        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(str.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }

    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    private static String create_nonce_str() {
        return UUID.randomUUID().toString();
    }

    private static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    //获取当前系统时间 用来判断access_token是否过期
    public static String getTime() {
        Date dt = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(dt);
    }

    public static void setTime(String time) {
        AdvancedUtil.time = time;
    }

    public static String getRequestCodeUrl(String redirectUrl) throws IOException {
        String APPID = AdvancedUtil.wxAppId;
        return String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect",
                APPID, redirectUrl, "snsapi_userinfo", "xxxx_state");
    }


    /**
     * URL编码（utf-8）
     *
     * @param source
     * @return
     */
    public static String urlEncodeUTF8(String source) {
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取网页授权凭证
     *
     * @param appId     公众账号的唯一标识
     * @param appSecret 公众账号的密钥
     * @param code
     * @return WeixinAouth2Token
     */
    public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
        WeixinOauth2Token wat = null;
        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        requestUrl = requestUrl.replace("APPID", appId);
        requestUrl = requestUrl.replace("SECRET", appSecret);
        requestUrl = requestUrl.replace("CODE", code);
        // 获取网页授权凭证
        JSONObject jsonObject = AdvancedUtil.httpsRequest(requestUrl, "GET", "");
        if (null != jsonObject) {
            try {
                wat = new WeixinOauth2Token();
                wat.setAccessToken(jsonObject.getString("access_token"));
                wat.setExpiresIn(jsonObject.getInteger("expires_in"));
                wat.setRefreshToken(jsonObject.getString("refresh_token"));
                wat.setOpenId(jsonObject.getString("openid"));
                wat.setScope(jsonObject.getString("scope"));
            } catch (Exception e) {
                wat = null;
                int errorCode = jsonObject.getInteger("errcode");
                String errorMsg = jsonObject.getString("errmsg");
//                log.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg);
            }
        }
        return wat;
    }

    /**
     * 获取接口访问凭证
     *
     * @param appid     凭证
     * @param appsecret 密钥
     * @return
     */
    public static Token getAccess_token(String appid, String appsecret) {
        Token token = new Token();


        //凭证获取(GET)
        String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
        // 发起GET请求获取凭证
        JSONObject jsonObject = AdvancedUtil.httpsRequest(requestUrl, "GET", null);
        String access_token = null;
        if (null != jsonObject) {
            try {
                access_token = jsonObject.getString("access_token");
                token.setAccessToken(access_token);
                token.setExpiresIn(jsonObject.getIntValue("expires_in"));
            } catch (JSONException e) {
                // 获取token失败
                logger.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
            }
        }
        return token;
    }

    /***
     * 默认从缓存中去token
     * @return
     */
    public static Token getAccessTokenUtil() {
        if (token == null) {
            time = getTime();
            token = AdvancedUtil.getAccess_token(AdvancedUtil.wxAppId, AdvancedUtil.wxAppSecret);
        } else {
            if (!time.substring(0, 13).equals(getTime().substring(0, 13))) { //每小时刷新一次
                token = AdvancedUtil.getAccess_token(AdvancedUtil.wxAppId, AdvancedUtil.wxAppSecret);
                time = getTime();
            }
        }
        return token;
    }

    /**
     * 调用微信JS接口的临时票据
     *
     * @param access_token 接口访问凭证
     * @return
     */
    public static String getJsApiTicket(String access_token) {
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
        String requestUrl = url.replace("ACCESS_TOKEN", access_token);
        // 发起GET请求获取凭证
        JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
        String ticket = null;
        if (null != jsonObject) {
            try {
                ticket = jsonObject.getString("ticket");
            } catch (JSONException e) {
                // 获取token失败
                logger.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
            }
        }
        return ticket;
    }

    /**
     * 发送https请求
     *
     * @param requestUrl    请求地址
     * @param requestMethod 请求方式（GET、POST）
     * @param outputStr     提交的数据
     * @return JSONObject(通过JSONObject.get ( key)的方式获取json对象的属性值)
     */
    public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        JSONObject jsonObject = null;
        try {
            // 创建SSLContext对象，并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            conn.setRequestMethod(requestMethod);

            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                System.out.println(outputStr);
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                System.out.println(str);
                buffer.append(str);
            }

            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            jsonObject = JSONObject.parseObject(buffer.toString());
        } catch (ConnectException ce) {
            logger.error("连接超时：{}", ce);
        } catch (Exception e) {
            logger.error("https请求异常：{}", e);
        }
        return jsonObject;
    }

    public static JSONObject httpsRequestWithUserAndPass(String requestUrl, String requestMethod, String outputStr,
                                                         String userName, String pass) {
        JSONObject jsonObject = null;
        try {
            // 创建SSLContext对象，并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();


            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);
            if (StringUtils.isNoneBlank(userName) && StringUtils.isNoneBlank(pass)) {
                String input = userName + ":" + pass;
                String encodingInput = Base64.getEncoder().encodeToString(input.getBytes());
                conn.setRequestProperty("Authorization", "Basic " + encodingInput);
            }

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            conn.setRequestMethod(requestMethod);

            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                System.out.println(outputStr);
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                System.out.println(str);
                buffer.append(str);
            }

            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            jsonObject = JSONObject.parseObject(buffer.toString());
        } catch (ConnectException ce) {
            logger.error("连接超时：{}", ce); //
        } catch (Exception e) {
            logger.error("https请求异常：{}", e);
        }
        return jsonObject;
    }

    public static Logger getLogger() {
        return logger;
    }

    /**
     * 通过网页授权获取用户信息
     *
     * @param accessToken 网页授权接口调用凭证
     * @param openId      用户标识
     * @return SNSUserInfo
     */
//    @SuppressWarnings( { "deprecation", "unchecked" })
//    public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {
//        SNSUserInfo snsUserInfo = null;
//        // 拼接请求地址
//        String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
//        requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
//        // 通过网页授权获取用户信息
//        JSONObject jsonObject = AdvancedUtil.httpsRequest(requestUrl, "GET", null);
//
//        if (null != jsonObject) {
//            try {
//                snsUserInfo = new SNSUserInfo();
//                // 用户的标识
//                snsUserInfo.setOpenId(jsonObject.getString("openid"));
//                // 昵称
//                snsUserInfo.setNickname(jsonObject.getString("nickname"));
//                // 性别（1是男性，2是女性，0是未知）
//                snsUserInfo.setSex(jsonObject.getInt("sex"));
//                // 用户所在国家
//                snsUserInfo.setCountry(jsonObject.getString("country"));
//                // 用户所在省份
//                snsUserInfo.setProvince(jsonObject.getString("province"));
//                // 用户所在城市
//                snsUserInfo.setCity(jsonObject.getString("city"));
//                // 用户头像
//                snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));
//                // 用户特权信息
//                snsUserInfo.setPrivilegeList(JSONArray.toList(jsonObject.getJSONArray("privilege"), List.class));
//            } catch (Exception e) {
//                snsUserInfo = null;
//                int errorCode = jsonObject.getInt("errcode");
//                String errorMsg = jsonObject.getString("errmsg");
////                log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, errorMsg);
//            }
//        }
//        return snsUserInfo;
//    }
    public static void setLogger(Logger logger) {
        AdvancedUtil.logger = logger;
    }

    public static Token getToken() {
        return token;
    }

    public static void setToken(Token token) {
        AdvancedUtil.token = token;
    }

    public static String getJsapi_ticket() {
        return jsapi_ticket;
    }

    public static void setJsapi_ticket(String jsapi_ticket) {
        AdvancedUtil.jsapi_ticket = jsapi_ticket;
    }

    public static String getWxAppId() {
        return wxAppId;
    }

    public static void setWxAppId(String wxAppId) {
        AdvancedUtil.wxAppId = wxAppId;
    }

    public static String getWxAppSecret() {
        return wxAppSecret;
    }

    public static void setWxAppSecret(String wxAppSecret) {
        AdvancedUtil.wxAppSecret = wxAppSecret;
    }

    public static String getPcAppId() {
        return pcAppId;
    }

    public static void setPcAppId(String pcAppId) {
        AdvancedUtil.pcAppId = pcAppId;
    }

    public static String getPcAppSecret() {
        return pcAppSecret;
    }

    public static void setPcAppSecret(String pcAppSecret) {
        AdvancedUtil.pcAppSecret = pcAppSecret;
    }

    public void init() {
        advancedUtil = this;
        advancedUtil.wxAppId = this.wxAppId;
        advancedUtil.wxAppSecret = this.wxAppSecret;
        advancedUtil.pcAppId = this.pcAppId;
        advancedUtil.pcAppSecret = this.pcAppSecret;
    }

    /**
     * 获取用户信息
     *
     * @param accessToken
     * @param openId
     * @return
     */
    public Map<String, String> getUserInfo(String accessToken, String openId) {
        Map<String, String> data = new HashMap();
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
        JsonObject userInfo = null;
        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(url);
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity httpEntity = httpResponse.getEntity();
            String response = EntityUtils.toString(httpEntity, "utf-8");
            Gson token_gson = new Gson();
            logger.info("【微信】", response);
            userInfo = token_gson.fromJson(response, JsonObject.class);
            logger.info("get userinfo success. [result={}]", userInfo);
            data.put("openid", userInfo.get("openid").toString().replaceAll("\"", ""));
            data.put("nickname", userInfo.get("nickname").toString().replaceAll("\"", ""));
            data.put("city", userInfo.get("city").toString().replaceAll("\"", ""));
            data.put("province", userInfo.get("province").toString().replaceAll("\"", ""));
            data.put("country", userInfo.get("country").toString().replaceAll("\"", ""));
            data.put("headimgurl", userInfo.get("headimgurl").toString().replaceAll("\"", ""));
            data.put("unionid", userInfo.get("unionid").toString().replaceAll("\"", ""));
            logger.debug("==============.", data.get("unionid"));

        } catch (Exception ex) {
            logger.error("fail to request wechat user info. [error={}]", ex);
        }
        return data;
    }

    /**
     * 获取请求用户信息的access_token
     * todo 缓存access_token
     *
     * @param type pc | wx
     * @param code
     * @return
     */
    public Map<String, String> getUserInfoAccessToken(String code, String type) {
        JsonObject object = null;
        Map<String, String> data = new HashMap();
        try {
            String APP_ID = "";
            String APP_SECRET = "";
            if ("pc".equals(type.toLowerCase())) {
                APP_ID = AdvancedUtil.pcAppId;
                APP_SECRET = AdvancedUtil.pcAppSecret;
            } else {
                APP_ID = AdvancedUtil.wxAppId;
                APP_SECRET = AdvancedUtil.wxAppSecret;
            }


            logger.error(APP_SECRET);
            logger.error(APP_ID);
            logger.error(code);
            String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
                    APP_ID, APP_SECRET, code);
            //create http client
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(url);
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity httpEntity = httpResponse.getEntity();
            String tokens = EntityUtils.toString(httpEntity, "utf-8");
            logger.error(tokens);
            Gson token_json = new Gson(); // Google parse Json framework
            object = token_json.fromJson(tokens, JsonObject.class);
            data.put("unionid", object.get("unionid").toString().replaceAll("\"", ""));
            data.put("openid", object.get("openid").toString().replaceAll("\"", ""));
            data.put("access_token", object.get("access_token").toString().replaceAll("\"", ""));
        } catch (Exception ex) {
            logger.error("fail to request wechat access token. [error={}]", ex);
        }
        return data;
    }

    public AdvancedUtil getAdvancedUtil() {
        return advancedUtil;
    }

    public void setAdvancedUtil(AdvancedUtil advancedUtil) {
        this.advancedUtil = advancedUtil;
    }
}
